﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using QueryBuilder.Controls.Configuration;
using QueryBuilder.Core.Visualization.QueryVisualization;


namespace QueryBuilder.Controls
{
    /// <summary>
    /// 
    /// </summary>
    [TemplatePart(Name = "ContentPresenter", Type = typeof (ContentPresenter))]
    public class ConstraintVisualizer : ContentControl
    {
        public static DependencyProperty ComparisonTemplateConfigurationProperty
            = DependencyProperty.Register("ComparisonTemplateConfiguration", typeof (ComparisonTemplateConfiguration),
                                          typeof (ConstraintVisualizer),
                                          new PropertyMetadata(OnComparisonTemplateConfigurationPropertyChanged));


        /// <summary>
        /// 
        /// </summary>
        public static DependencyProperty ConstraintProperty
            = DependencyProperty.Register("VisualizableConstraint", typeof(VisualizableConstraint),
                                          typeof (ConstraintVisualizer),
                                          new PropertyMetadata(OnConstraintPropertyChanged));

        public static DependencyProperty VisualizationConfigProperty
            = DependencyProperty.Register("VisualizationConfig", typeof (VisualizationConfig),
                                          typeof (ConstraintVisualizer),
                                          new PropertyMetadata(OnVisualizationConfigPropertyChanged));

        private ComplexDetector complexDetector = new ComplexDetector();

        private DataFieldInfoFactory dataFieldInfoFactory;
        protected ExpressionFactory expressionFactory;


        /// <summary>
        /// Initializes the <see cref="ConstraintVisualizer"/> class.
        /// </summary>
        static ConstraintVisualizer()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof (ConstraintVisualizer), new FrameworkPropertyMetadata(typeof (ConstraintVisualizer)));
        }

        protected virtual ExpressionFactory ExpressionFactory
        {
            get
            {
                if (this.expressionFactory == null)
                {
                    this.expressionFactory = new ExpressionFactory(this.ComparisonTemplateConfiguration);
                }
                return this.expressionFactory;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        protected virtual DataFieldInfoFactory DataFieldFactory
        {
            get
            {
                if (this.dataFieldInfoFactory == null)
                {
                    this.dataFieldInfoFactory = new DataFieldInfoFactory(this.VisualizationConfig, this.ComparisonTemplateConfiguration);
                }

                return this.dataFieldInfoFactory;
            }
        }


        /// <summary>
        /// Gets or sets the visualization config.
        /// </summary>
        /// <value>The visualization config.</value>
        public VisualizationConfig VisualizationConfig
        {
            get { return (VisualizationConfig) this.GetValue(VisualizationConfigProperty); }
            set { this.SetValue(VisualizationConfigProperty, value); }
        }


        /// <summary>
        /// Gets or sets the comparison template configuration.
        /// </summary>
        /// <value>The comparison template configuration.</value>
        public ComparisonTemplateConfiguration ComparisonTemplateConfiguration
        {
            get { return (ComparisonTemplateConfiguration) this.GetValue(ComparisonTemplateConfigurationProperty); }
            set { this.SetValue(ComparisonTemplateConfigurationProperty, value); }
        }

        /// <summary>
        /// Gets or sets the expression.
        /// </summary>
        /// <value>The expression.</value>
        public VisualizableConstraint VisualizableConstraint
        {
            get { return (VisualizableConstraint)this.GetValue(ConstraintProperty); }
            set { this.SetValue(ConstraintProperty, value); }
        }

        private static void OnComparisonTemplateConfigurationPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var expressionVisualizer = d as ConstraintVisualizer;
            if (expressionVisualizer != null)
            {
                if (expressionVisualizer.VisualizationConfig != null && expressionVisualizer.VisualizableConstraint != null)
                {
                    expressionVisualizer.GenerateFields();
                }
            }
        }

        private static void OnVisualizationConfigPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var expressionVisualizer = d as ConstraintVisualizer;
            if (expressionVisualizer != null)
            {
                    if (expressionVisualizer.ComparisonTemplateConfiguration != null && expressionVisualizer.VisualizableConstraint != null)
                    {
                        expressionVisualizer.GenerateFields();
                    }
            }
        }

        private void GenerateFields()
        {
            this.GenerateExpression(this.VisualizableConstraint);
        }

        /// <summary>
        /// Called when [expression property changed].
        /// </summary>
        /// <param name="d">The d.</param>
        /// <param name="e">The <see cref="System.Windows.DependencyPropertyChangedEventArgs"/> instance containing the event data.</param>
        private static void OnConstraintPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var expressionVisualizer = d as ConstraintVisualizer;
            if (expressionVisualizer != null)
            {

                if (expressionVisualizer.VisualizableConstraint != null)
                {
                    expressionVisualizer.VisualizableConstraint.PropertyChanged -= expressionVisualizer.OnConstraintPropertyChanged;
                }
                if (e.NewValue != null)
                {
                    var expression = e.NewValue as VisualizableConstraint;

                    expression.PropertyChanged += expressionVisualizer.OnConstraintPropertyChanged;
                }
                if (expressionVisualizer.VisualizationConfig != null && expressionVisualizer.ComparisonTemplateConfiguration != null)
                {
                    expressionVisualizer.GenerateFields();
                }

            }
        }


        private void OnConstraintPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            this.Content = null;
            this.GenerateExpression(this.VisualizableConstraint);
        }

        /// <summary>
        /// Generates the expression.
        /// </summary>
        /// <param name="expression">The expression.</param>
        private void GenerateExpression(VisualizableConstraint expression)
        {
            this.FormatExpressionForComaprison();

            List<DataField> dataFields = this.GetDataFields(expression);

            DataField comparisonDataField = this.GenerateDataField(this.GetComparisonInfo(), expression);

            FrameworkElement expressionVisualizer = null;
            if (expression is VisualizableExpression)
            {
                var expressionVisualizable = expression as VisualizableExpression;
                expressionVisualizer = this.GetVisualizer(dataFields, comparisonDataField, expressionVisualizable.Comparison);
            }
            else
            {
                expressionVisualizer = this.GetVisualizer(dataFields);
            }

            this.Content = expressionVisualizer;
        }


        private void FormatExpressionForComaprison()
        {
            if (this.VisualizableConstraint is VisualizableExpression)
            {
                var expressionVisualizable = this.VisualizableConstraint as VisualizableExpression;
                IEnumerable<TemplateSection> templateSections = this.ComparisonTemplateConfiguration.GetComparisonTemplate(expressionVisualizable.Comparison);

                if ((this.VisualizableConstraint.Constraints.Count() + 1) != templateSections.Count())
                {
                    while (this.VisualizableConstraint.Constraints.Count() > (templateSections.Count() - 1))
                    {
                        this.VisualizableConstraint.Constraints.Remove(this.VisualizableConstraint.Constraints.Last());
                    }
                    while (this.VisualizableConstraint.Constraints.Count() < (templateSections.Count() - 1))
                    {
                        this.VisualizableConstraint.Constraints.Add(new VisualizableVariable<string>("  "));
                    }
                }
            }
        }

        private List<DataField> GetDataFields(VisualizableConstraint expression)
        {
            var dataFields = new List<DataField>();
            if (expression is VisualizableExpression)
            {
                foreach (VisualizableConstraint constraint in expression.Constraints)
                {
                    DataFieldInfo dataFieldInfo = this.GenerateDataFieldInfo(constraint);
                    if (dataFieldInfo.DisplayControlFactory != null && dataFieldInfo.EditControlFactory != null)
                    {
                        DataField dataField = this.GenerateDataField(dataFieldInfo, constraint);
                        dataField.IsComplex = dataFieldInfo.IsComplex;
                        dataFields.Add(dataField);
                    }
                }
            }
            else
            {
                DataFieldInfo dataFieldInfo = this.GenerateDataFieldInfo(expression);
                DataField dataField = this.GenerateDataField(dataFieldInfo, expression);
                dataFields.Add(dataField);
            }

            return dataFields;
        }

        private DataField GenerateDataField(DataFieldInfo dataFieldInfo, object dataContextObject)
        {
            return new DataField
                       {
                           EditControlFactory = dataFieldInfo.EditControlFactory,
                           DisplayControlFactory = dataFieldInfo.DisplayControlFactory,
                           DataContext = dataContextObject,
                       };
        }

        private DataFieldInfo GetComparisonInfo()
        {
            return this.DataFieldFactory.GetComparisonInfo();
        }

        private FrameworkElement GetVisualizer(List<DataField> dataFieldInfos)
        {
            if (dataFieldInfos.Count() > 0)
            {
                return this.ExpressionFactory.GetVisualizer(dataFieldInfos.First());
            }
            return null;
        }

        private FrameworkElement GetVisualizer(List<DataField> dataFields, DataField comparisonDataField, string comparisonName)
        {
            return this.ExpressionFactory.GetVisualizer(comparisonDataField, dataFields, comparisonName);
        }

        /// <summary>
        /// Gets the data field factoreis.
        /// </summary>
        /// <param name="expression">The expression.</param>
        /// <returns></returns>
        private DataFieldInfo GenerateDataFieldInfo(VisualizableConstraint visualizableConstraint)
        {
            return this.DataFieldFactory.GetDataFieldInfo(visualizableConstraint);
        }
    }

    public class ComplexDetector : IVisualizableConstraintVisitor
    {
        private bool isComplex;

        public void Visit<T>(VisualizableVariable<T> visualizable)
        {
            this.isComplex = false;
        }

        public void Visit(VisualizableExpression visualizableExpression)
        {
            this.isComplex = true;
        }

        public void Visit(VisualizableCondition expression)
        {
            this.isComplex = true;
        }

        public bool IsComplex(VisualizableExpression visualizable)
        {
            this.isComplex = false;
            if (visualizable.Constraints != null)
            {
                foreach (var constraint in visualizable.Constraints)
                {
                    if (isComplex)
                    {
                        return true;
                    }
                    constraint.Accept(this);

                }
            }
            return this.isComplex;
        }
    }
}